package com.corticon.dialogs.web;

import java.util.Map;
import java.net.URLDecoder;
import java.io.UnsupportedEncodingException;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;

import org.springframework.context.MessageSource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.util.HtmlUtils;

import com.corticon.dialogs.dao.MessageDAO;
import com.corticon.dialogs.dao.UserDAO;
import com.corticon.dialogs.model.Auditable;
import com.corticon.dialogs.util.MapUtils;
import com.corticon.dialogs.util.Pager;
import com.corticon.dialogs.util.security.CustomUserDetails;
import com.corticon.dialogs.util.security.SecurityUtils;
import com.corticon.dialogs.validation.ValidationMessageFormatter;

/**
 * @author Lifan Yang
 */
public abstract class BaseController {

    protected String requestSuffix = ".html";
    protected String SUBMIT_ACTION = "submitAction";
    protected final String OPERATION_KEY = "OPERATION";
    protected final String FROM_PARAM = "from";

    // Submit Actions
    protected static final String SUBMIT = "SUBMIT";
    protected static final String SUBMIT_AND_NEW = "SUBMIT_AND_NEW";
    protected static final String SUBMIT_AND_EXIT = "SUBMIT_AND_EXIT";
    protected static final String SUBMIT_AND_VIEW = "SUBMIT_AND_VIEW";

    // Message Constants
    protected String[] defaultMessageKeys = {"message"};
    protected final String DEFAULT_MESSAGE_KEY = "message";

    // Error Constants
    protected String errorsViewName = "errors";
    protected final String ERROR_MESSAGE_KEY = "errors";
    protected String entityNotFoundViewName = "entityNotFound";
    protected String redirectPrefix = "redirect:";
    // Other Constants
    protected String PAGER_NAME = "pager";

    // RequestURIs
    protected static final String CREATE_REQUEST = "create";
    protected static final String EDIT_REQUEST = "edit";
    protected static final String LIST_REQUEST = "list";
    protected static final String VIEW_REQUEST = "view";
    protected static final String CANCEL_REQUEST = "cancel";
    protected static final String LAST_REQUEST_URL = "LAST_REQUEST_URL";

    // Flags
    protected static final String CREATE_FLAG = "IS_CREATE";
    protected static final String EDIT_FLAG = "IS_EDIT";

    // DAO
    @Resource
    protected UserDAO userDAO;

    // Other Resources
    @Resource
    protected MessageSource messageSource;
    @Resource
    protected ValidationMessageFormatter validationMessageFormatter;
    @Resource
    private MessageDAO messageDAO;
    // Message
    protected static final String publicFoundMessage = "public.found";
    protected static final String publicNotFoundMessage = "public.not.found";

    /**
     * Get the default view name according to the request path.
     *
     * @param request
     * @return
     */
    protected String getViewNameByRequestURI(HttpServletRequest request) {

        String viewName = "";

        String contextPath = request.getContextPath();
        String requestURI = request.getRequestURI();

        // Remove the contextPath from the requestURI.
        requestURI = StringUtils.removeStart(requestURI, contextPath);

        // Remove the request suffix from the requestURI.
        requestURI = StringUtils.removeEnd(requestURI, requestSuffix);

        if (requestURI != null && !requestURI.equals("")) {
            if (requestURI.startsWith("/")) {
                requestURI = StringUtils.substring(requestURI, 1);
            }

            if (requestURI.endsWith("/")) {
                requestURI = requestURI.substring(0, requestURI.length() - 1);
            }

            return StringUtils.replace(requestURI, "/", ".");
        }

        return viewName;
    }

    /**
     * Common redirect strategy.
     * <p/>
     * TODO: Make the number of the params cannot changed.
     *
     * @param submitAction
     * @return
     */
    protected String redirectBySubmitAction(String submitAction, String param) {
        if (SUBMIT.equalsIgnoreCase(submitAction)) {
            return redirectPrefix + VIEW_REQUEST + requestSuffix + "?" + param;
        }
        if (SUBMIT_AND_NEW.equalsIgnoreCase(submitAction)) {
            return redirectPrefix + CREATE_REQUEST + requestSuffix;
        }
        if (SUBMIT_AND_EXIT.equalsIgnoreCase(submitAction)) {
            return redirectPrefix + LIST_REQUEST + requestSuffix + "?checkSearch=list";
        }
        if (SUBMIT_AND_VIEW.equalsIgnoreCase(submitAction)) {
            return redirectPrefix + VIEW_REQUEST + requestSuffix + "?" + param;
        }
        return "";
    }

    /**
     * This method will redirect to a target page according to the 'target' param. The 'from' param will be passed to the
     * target page as redirect.
     *
     * @param target
     * @param param
     * @param request
     * @return
     */
    protected String redirectBySubmitAction(String target, String param, HttpServletRequest request) {
        String startingPoint = "";
        if (request != null && StringUtils.isNotEmpty(request.getParameter(FROM_PARAM))) {
            startingPoint = request.getParameter(FROM_PARAM);
        }

        if (SUBMIT.equalsIgnoreCase(target)) {
            return redirectPrefix + VIEW_REQUEST + requestSuffix + "?" + param + "&from=" + startingPoint;
        } else if (SUBMIT_AND_NEW.equalsIgnoreCase(target)) {
            return redirectPrefix + CREATE_REQUEST + requestSuffix + "?from=" + startingPoint;
        } else if (SUBMIT_AND_EXIT.equalsIgnoreCase(target)) {
            if (StringUtils.isNotEmpty(startingPoint)) {
                try {
                    startingPoint = URLDecoder.decode(startingPoint, "utf-8");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                    return redirectPrefix + LIST_REQUEST + requestSuffix;
                }
                return redirectPrefix + startingPoint;
            } else {
                return redirectPrefix + LIST_REQUEST + requestSuffix;
            }
        } else if (SUBMIT_AND_VIEW.equalsIgnoreCase(target)) {
            return redirectPrefix + VIEW_REQUEST + requestSuffix + "?" + param + "&from=" + startingPoint;
        } else
            return redirectPrefix + LIST_REQUEST + requestSuffix;
    }

    /**
     * Add messages to SessionScope.
     *
     * @param messagesMap
     * @param request
     * @param messageSource
     */
    protected void addMessages(Map<String, String> messagesMap, HttpServletRequest request, MessageSource messageSource,
                               Object[] args) {

        for (Map.Entry<String, String> messageEntry : messagesMap.entrySet()) {
            /**
             * modify by heweiya old way:from messagesource new way:from
             * dabatase
             */
            /*
             * String message =
             * messageSource.getMessage(messageEntry.getValue(), args, request.
             * getLocale());
             */
            String message = this.getMessage("", messageEntry.getValue());
            request.getSession().setAttribute(messageEntry.getKey(), message);
        }
    }

    /**
     * Add messages to SessionScope. Message sample:
     * messageKey1=message1&messageKey2=message2
     *
     * @param messages
     * @param request
     * @param messageSource
     */
    protected void addMessages(String messages, HttpServletRequest request, MessageSource messageSource, Object[] args) {
        Map<String, String> messagesMap = MapUtils.delimitedNameValuesStringToMap(messages, "&", "=");
        addMessages(messagesMap, request, messageSource, args);
    }

    /**
     * Get the messages from SessionScope according to the messageKeys, put
     * these messages into RequestScope. Then clean these messages from
     * SessionScope.
     *
     * @param messageKeys
     * @param request
     */
    protected void readMessages(String[] messageKeys, HttpServletRequest request) {
        for (String messageKey : messageKeys) {
            String message = (String) request.getSession().getAttribute(
                    messageKey);

            if (StringUtils.isNotBlank(message)) {
                request.setAttribute(messageKey, message);
                request.getSession().removeAttribute(messageKey);
            }
        }
    }

    /**
     * Remove messages from http session
     *
     * @param messageKeys
     * @param request
     */
    protected void cleanMessages(String[] messageKeys, HttpServletRequest request) {
        for (String messageKey : messageKeys) {
            request.getSession().removeAttribute(messageKey);
        }
    }

    /**
     * Default method used to cancel an action. Cancel operation can return to the target you want
     * to by the "from" param.
     *
     * @param request
     * @return
     */
    @RequestMapping(value = CANCEL_REQUEST, method = RequestMethod.GET)
    protected String cancel(HttpServletRequest request) {
        String fromUrl = request.getParameter(FROM_PARAM);

        boolean hasMsg = true;
        if (StringUtils.isNotEmpty(request.getParameter("hasMsg")))
            hasMsg = Boolean.parseBoolean(request.getParameter("hasMsg"));

        if (hasMsg)
            request.getSession().setAttribute(DEFAULT_MESSAGE_KEY, getMessage("general.action.cancel"));

        if (StringUtils.isNotBlank(fromUrl))
            return redirectPrefix + fromUrl;
        else
            return redirectPrefix + LIST_REQUEST + requestSuffix;
    }

    /**
     * Check that whether the current operation is a create operation.
     *
     * @param request
     * @return
     */
    protected boolean isCreate(HttpServletRequest request) {
        return request.getAttribute(CREATE_FLAG) != null;
    }

    /**
     * Mark the operation is to create an object.
     *
     * @param request
     */
    protected void setCreate(HttpServletRequest request) {
        request.setAttribute(CREATE_FLAG, true);
        request.setAttribute(OPERATION_KEY, CREATE_REQUEST);
    }

    /**
     * Check that whether the current operation is an edit operation.
     *
     * @param request
     * @return
     */
    protected boolean isEdit(HttpServletRequest request) {
        return request.getAttribute(EDIT_FLAG) != null;
    }

    /**
     * Mark the operation is to edit an object.
     *
     * @param request
     */
    protected void setEdit(HttpServletRequest request) {
        request.setAttribute(EDIT_FLAG, true);
        request.setAttribute(OPERATION_KEY, EDIT_REQUEST);
    }

    /**
     * Construct the pager object.
     *
     * @param page
     * @param request
     * @return
     */
    protected Pager constructPager(Integer page, HttpServletRequest request) {
        page = page == null ? 1 : page;
        Pager pager = new Pager();
        // Set the current page number.
        pager.setPage(page);
        pager.setRootUrl(request.getRequestURI());

        String sortColumn = request.getParameter("sortColumn");
        if (sortColumn != null) {
            pager.setRequestParameters(pager.getRequestParameters() + "&sortColumn=" + sortColumn);
        }

        String sortBy = request.getParameter("sortBy");
        if (sortBy != null) {
            pager.setRequestParameters(pager.getRequestParameters() + "&sortBy=" + sortBy);
        }

        return pager;
    }

    protected String redirectTo403() {
        return redirectPrefix + "/403.jsp";
    }

    protected void setModifiedByInfo(Auditable model) {
        CustomUserDetails currentUser = SecurityUtils.getCurrentUser();
        model.setModifiedBy(userDAO.load(currentUser.getUsername()));
    }

    protected String entityNotFound() {
        return entityNotFoundViewName;
    }

    protected String getMessage(String fieldName, String messageKey) {
        return messageDAO.getMessage(fieldName, messageKey, "", "");
    }

    /**
     * This method loads a message for the database according to the message key. Then replace the
     * placeholder with the param "args".
     *
     * @param messageKey
     * @param args
     * @return message from the database
     */
    protected String getMessage(String messageKey, Object... args) {
        String message = messageDAO.findByKey(messageKey).getValue();
        if (StringUtils.isNotEmpty(message)) {
            if (args != null) {
                for (int i = 0; i < args.length; i++) {
                    message.replace("{" + i + "}", args[i].toString());
                }
            }
        }
        return message;
    }

    protected Pager setPagerSort(Pager pager, String defaultColumn, HttpServletRequest request) {
        String sortColumn = "";
        String sortBy = "desc";
        if (request.getParameterValues("sortColumn") != null) {
            sortColumn = request.getParameterValues("sortColumn")[0];
        }
        if (request.getParameterValues("sortBy") != null) {
            sortBy = request.getParameterValues("sortBy")[0];
        }
        sortColumn = sortColumn == "" ? defaultColumn : sortColumn;

        pager.setSortColumn(sortColumn);
        if (sortBy.equals("desc")) {
            pager.setSortAsc(false);
        } else {
            pager.setSortAsc(true);
        }

        return pager;
    }
}
